﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Tab2Sql.Forms
{
    class HowSimilar
    {

        public string MostSimilar(string needle, List<string> haystack)
        {
            long[] scores = new long[haystack.Count];
            long currentBestScore = 0;
            string currentBest = String.Empty;
            for (int i = 0; i < haystack.Count; i++)
            {
                scores[i] = howSimilarX(needle, haystack[i]);
                if (scores[i] > currentBestScore)
                {
                    currentBestScore = scores[i];
                    currentBest = haystack[i];
                }
            }


            return currentBest;
        }









        public static long howSimilarX(string x, string y)
        {
            x = cleanValue(x);
            y = cleanValue(y);

            if (x != null && y != null && x.Length > 0 && y.Length > 0)
            {
                char[] myArray1 = x.ToCharArray();
                char[] myArray2 = y.ToCharArray();
                int lenX = x.Length;
                int lenY = y.Length;
                int earlyBirdBonus = (lenX + lenY);
                int divergancePenalty = Math.Abs(lenX - lenY);
                int totLength = lenX + lenY;
                int posX = 0;
                int posY = 0;
                long score = 0;
                decimal multiplier = (decimal)1.0;
                long lastScore = 0;
                bool lastResult = false;
                bool present = false;
                long iCounter = lenX * lenY;
                if (x == y)
                {   //strings perfectly match, so skip rest of method.
                    return 10000000;
                }
                if ((x.Substring(0, 5)) == (y.Substring(0, 5)))
                {
                    score = score + 3000;
                }
                if ((x.Substring(0, 4)) == (y.Substring(0, 4)))
                {
                    score = score + 2000;
                }
                if ((x.Substring(0, 3)) == (y.Substring(0, 3)))
                {
                    score = score + 1000;
                }
                if ((x.Substring(0, 2)) == (y.Substring(0, 2)))
                {
                    score = score + 500;
                }



                foreach (char c in myArray1)
                {
                    present = false;
                    foreach (char d in myArray2)
                    {

                        iCounter--;
                  

                        if (c == d) // we have a match
                        {
                            score = score + iCounter;
                            present = true;
                            if (lastResult == true)
                            {
                                multiplier = Math.Min((multiplier * (decimal)1.15),10000);
                            }
                            else
                            {
                                multiplier = multiplier * (decimal)0.85;
                            }
                            if (posX == posY)
                            {
                                score = score + (long)((decimal)10 * multiplier);  // bonus if the match is in the same position in both strings
                            }
                            score = score + earlyBirdBonus;
                            // the formula gives additional weight to character matches near the start of the thread.
                            score = score + (totLength - (Math.Abs(posX - posY))) + (long)((decimal)5 * multiplier);

                            // ScrabbleBonus gives additional weight to rarer constanants versus common vowels
                            // rationale : people miss out vowels more often when shortening words - Road => Rd, Court=>Ct etc.
                            score = score + (scrabbleBonus(c) * 4);
                        }
                        posY++;
                    }
                    posX++;
                    posY = 0;
                    if (present == false) // no match, so deduct points from score
                    {
                        score = score - (scrabbleBonus(c) * 3);
                        score = score - 150;                     // arbritrary. Will experiment. 
                        score = score - (iCounter * 2);
                    }
                    lastScore = score;
                    lastResult = present;
                    earlyBirdBonus--;
    
                }
                score = score - (long)(divergancePenalty * totLength) / 2;

                return Convert.ToInt64(score / totLength);
            }
            return -9999; // error
        }

        private static string cleanValue(string x)
        {
            x = DGen.DText.RemoveWhiteSpace(x);
            char[] badChars = new char[] { ',', '£', '$', '%', ' ', '\'', ':', ';', '^', '&', '*', '"' , ')','(','.'};
            foreach (char udc in badChars)
            {
                x = x.Replace(udc.ToString(),String.Empty);
            }
            return x.ToUpper();
        }

        private enum Alphabet
        {
            a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z
        }
        // incidental - test use only
        /// <summary>
        /// how many scrabble points is this letter worth?
        /// </summary>
        /// <param name="c"></param>
        /// <returns></returns>
        private static int scrabbleBonus(char c)
        {
            int points = 0;
            Alphabet let;
            let = (Alphabet)WhereInAlpha(c);
            switch (let)
            {
                case Alphabet.a:
                    points = 1;
                    break;
                case Alphabet.b:
                    points = 2;
                    break;
                case Alphabet.c:
                    points = 2;
                    break;
                case Alphabet.d:
                    points = 2;

                    break;
                case Alphabet.e:
                    points = 1;
                    break;
                case Alphabet.f:
                    points = 1;
                    break;
                case Alphabet.g:
                    points = 2;
                    break;
                case Alphabet.h:
                    points = 4;
                    break;
                case Alphabet.i:
                    points = 1;
                    break;
                case Alphabet.j:
                    points = 8;
                    break;
                case Alphabet.k:
                    points = 5;
                    break;
                case Alphabet.l:
                    points = 1;
                    break;
                case Alphabet.m:
                    points = 2;
                    break;
                case Alphabet.n:
                    points = 1;
                    break;
                case Alphabet.o:
                    points = 1;
                    break;
                case Alphabet.p:
                    points = 2;
                    break;
                case Alphabet.q:
                    points = 10;
                    break;
                case Alphabet.r:
                    points = 1;
                    break;
                case Alphabet.s:
                    points = 1;
                    break;
                case Alphabet.t:
                    points = 1;
                    break;
                case Alphabet.u:
                    points = 1;
                    break;
                case Alphabet.v:
                    points = 4;
                    break;
                case Alphabet.w:
                    points = 3;
                    break;
                case Alphabet.x:
                    points = 8;
                    break;
                case Alphabet.y:
                    points = 4;
                    break;
                case Alphabet.z:
                    points = 10;
                    break;
                default:
                    // default = numbers
                    points = 15;
                    break;
            }
            return points;
        }
        private static int WhereInAlpha(char Letter)
        {
            string alpha = "abcdefghijklmnopqrstuvwxyz";
            return alpha.IndexOf(Letter);
        }
    }
}
